﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dadge.Interfaces.Common;
using System.Threading.Tasks;
using System.Threading;
using Dadge.Services.Common.Pattern.Pipeline;

namespace Dadge.Services.Common.Pattern.Pipeline
{
    public class PipelineHelper
    {
        private ConcurrentQueue<DataCommand> _queue;
        private Dictionary<Guid, Task> _previousTasks;
        private Action _finish;

        public PipelineHelper()
        {
            _queue = new ConcurrentQueue<DataCommand>();
            _previousTasks = new Dictionary<Guid, Task>();
        }

        public PipelineHelper AddTask(Action action, TaskScheduler scheduler, out Guid newId)
        {
            return AddTask(action, Guid.Empty, scheduler, out newId);
        }

        public PipelineHelper AddTask(Action action, Guid previousCmdId, out Guid newId)
        {
            return AddTask(action, previousCmdId, TaskScheduler.Default, out newId);
        }
        public PipelineHelper AddTask(Action action, Guid previousCmdId, TaskScheduler scheduler, out Guid newId)
        {
            var dataCmd = new DataCommand() { Action = action, Scheduler = scheduler, PreviousCmdId = previousCmdId };
            _queue.Enqueue(dataCmd);
            Process();
            newId = dataCmd.Id;
            return this;
        }
        public PipelineHelper WhenFinish(Action action)
        {
            _finish = action;
            return this;
        }

        private void Process()
        {
            if (_queue.Count == 0)
            {
                _finish();
            }
            DataCommand current;
            if (_queue.TryDequeue(out current))
            {
                var currentTask = current.Action;
                var currentScheduler = current.Scheduler;         
                var previousTask = _previousTasks.ContainsKey(current.PreviousCmdId) ? _previousTasks.FirstOrDefault(x => x.Key == current.PreviousCmdId).Value : null;
                Task task;
                if (previousTask != null && !previousTask.IsCompleted)
                {
                    task = previousTask.ContinueWith(o =>
                    {
                        currentTask();
                    }
                , currentScheduler);
                }
                else
                {
                    task = Task.Factory.StartNew(() =>
                        {
                            currentTask();
                        }
                        , CancellationToken.None, TaskCreationOptions.None, currentScheduler);
                }
                _previousTasks.Add(current.Id, task);
            }
        }
    }
}
